/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2009>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Drawing;
using MyPhotoIndex.Utilities.ImageMetaData;
using MyPhotoIndex.Interfaces;
using MyPhotoIndex.Utilities;
using System.IO;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Configuration;
using System.ComponentModel;
using System.Reflection;
using MyPhotoIndex.Interfaces.ImageFormatProviders;
using MyPhotoIndex.Interfaces.Imaging;

namespace MyPhotoIndex.Plugins.ImageFormatProviders
{
    public class ImageFormatProvider : IImageFormatProvider
    {
        private string m_extention;

        public virtual bool IsVideo
        {
            get { return false; }
        }

        public virtual bool IsCachable
        {
            get { return true; }
        }

        public virtual bool CanSave
        {
            get { return true; }
        }

        public ImageFormatProvider(string extention)
        {
            m_extention = extention;
        }

        public string Extention
        {
            get { return m_extention; }
            //set { m_extention = value; }
        }

        #region IImageFormatProvider Members

        public virtual Image GetThumbnail(string fileName, Size size)
        {
            try
            {
                Image thumbnail = null;

                if (string.IsNullOrEmpty(fileName) == true ||
                    File.Exists(fileName) == false)
                {
                    return null;
                }

                bool isLandscape = ImageAlgo.IsLandscapeImage(size);
                if ((isLandscape == true && size.Width <= 240) ||
                    isLandscape == false && size.Height <= 160)
                {
                    ExifExtractor exif = new ExifExtractor(fileName);
                    thumbnail = ThumbnailUtilities.ThumbnailFromByteArray(exif.Thumbnail);
                }

                //There is no embeded thumbnail in the images. so create one
                if (thumbnail == null)
                {
                    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    {
                        using (Image image = Image.FromStream(fs, false, false))
                        {
                            Size calculatedSize = ImageAlgo.GetImageProportionalSize(new SizeF(image.Width, image.Height), new SizeF(size.Width, size.Height));
                            thumbnail = new Bitmap(image, calculatedSize.Width, calculatedSize.Height);
                        }
                    }
                }

                return thumbnail;
            }
            catch { }

            return null;
        }

        public virtual bool CanRotate
        {
            get { return true; }
        }

        public virtual void Rotate(string fileName, RotateFlipType rotationType, out Image thumbnail, out Size newImageSize)
        {
            thumbnail = null;
            newImageSize = Size.Empty;

            using (MemoryStream ms = new MemoryStream())
            {
                using (Image image = Image.FromFile(fileName))
                {
                    EncoderValue transformEncoderValue = EncoderValue.TransformRotate90;
                    switch (rotationType)
                    {
                        case RotateFlipType.Rotate270FlipNone:
                            transformEncoderValue = EncoderValue.TransformRotate270;
                            break;
                        case RotateFlipType.Rotate90FlipNone:
                            transformEncoderValue = EncoderValue.TransformRotate90;
                            break;
                    }

                    Encoder encoder = Encoder.Transformation;
                    EncoderParameters encoderParms = new EncoderParameters(1);
                    EncoderParameter encoderParm;
                    ImageCodecInfo codecInfo = GetEncoderInfo(Path.GetExtension(fileName));

                    encoderParm = new EncoderParameter(encoder, (long)transformEncoderValue);
                    encoderParms.Param[0] = encoderParm;

                    image.Save(ms, codecInfo, encoderParms);
                }

                using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate | FileMode.Truncate, FileAccess.ReadWrite))
                {
                    byte[] data = new byte[ms.Length];
                    ms.Position = 0;
                    int i = ms.Read(data, 0, (int)ms.Length);
                    fs.Write(data, 0, data.Length);
                }

                using (Image image = Image.FromFile(fileName))
                {
                    newImageSize = image.Size;

                    Size calculatedSize = ImageAlgo.GetImageProportionalSize(new SizeF(image.Width, image.Height), new SizeF(320, 240));
                    thumbnail = image.GetThumbnailImage(calculatedSize.Width, calculatedSize.Height, null, IntPtr.Zero);
                }
            }
        }

        public static ImageCodecInfo GetEncoderInfo(String extention)
        {
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (int j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].FilenameExtension.ToLower().IndexOf(extention.ToLower()) > -1)
                {
                    return encoders[j];
                }
            }

            return null;
        }

        public Image LoadImage(IImageData imageData)
        {
            return LoadImage(imageData, Size.Empty);
        }

        public virtual Image LoadImage(IImageData imageData, Size requestedSize)
        {
            try
            {
                string fileName = imageData.FileName;

                if (string.IsNullOrEmpty(fileName) == true ||
                    File.Exists(fileName) == false)
                {
                    return null;
                }


                if (requestedSize == Size.Empty)
                {
                    Image image = Image.FromFile(fileName, true);
                    return image;
                }

                using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    using (Image image = Image.FromStream(fs, true, false))
                    {
                        try
                        {
                            Size realPicSize = ImageAlgo.GetImageProportionalSize(new SizeF(image.Width, image.Height), new SizeF(requestedSize.Width, requestedSize.Height));
                            Image resizedImage = new Bitmap(realPicSize.Width, realPicSize.Height);
                            using (Graphics graphic = Graphics.FromImage(resizedImage))
                            {
                                graphic.CompositingQuality = CompositingQuality.HighSpeed;
                                graphic.SmoothingMode = SmoothingMode.HighSpeed;
                                graphic.InterpolationMode = InterpolationMode.Bicubic;
                                Rectangle rectangle = new Rectangle(0, 0, realPicSize.Width, realPicSize.Height);
                                graphic.DrawImage(image, rectangle);
                            }

                            CopyImageProperties(image, resizedImage);

                            return resizedImage;
                        }
                        catch (Exception ex)
                        {
                            Debug.Write(ex);
                        }

                        return null;
                    }
                }
            }
            catch (FileNotFoundException)
            {
                return null;
            }
            catch (OutOfMemoryException)
            {
                return null;
            }
        }

        private void CopyImageProperties(Image sourceImage, Image destinationImage)
        {
            try
            {
                foreach (PropertyItem item in sourceImage.PropertyItems)
                {
                    destinationImage.SetPropertyItem(item);
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
            }
        }

        public virtual void SaveImage(Image image, IImageData imageData, String fileName)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                SaveImage(image, imageData, ms);
                using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite))
                {
                    byte[] data = new byte[ms.Length];
                    ms.Position = 0;
                    int i = ms.Read(data, 0, (int)ms.Length);
                    fs.Write(data, 0, data.Length);
                }

                if (string.Compare(imageData.FileName, fileName) == 0)
                {
                    SetImageMetaData(imageData, fileName);
                }
            }
        }

        public virtual void SaveImage(Image image, IImageData imageData, Stream stream)
        {
            ImageCodecInfo codecInfo = GetEncoderInfo(m_extention);
            EncoderParameters encoderParms = new EncoderParameters(1);

            using (Image drawnBitmap = new Bitmap(image.Width, image.Height, image.PixelFormat))
            {
                using (Graphics g = Graphics.FromImage(drawnBitmap))
                {
                    g.CompositingQuality = CompositingQuality.HighQuality;
                    g.SmoothingMode = SmoothingMode.HighQuality;
                    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    Rectangle rectangle = new Rectangle(0, 0, image.Width, image.Height);
                    g.DrawImage(image, rectangle);
                }

                try
                {
                    CopyImageProperties(image, drawnBitmap);

                    encoderParms.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
                    drawnBitmap.Save(stream, codecInfo, encoderParms);
                }
                catch (Exception ex1)
                {
                    Logger.Log(ex1);
                }
            }
        }

        public virtual IImageMetaData GetImageMetaData(string fileName)
        {
            FormatProviderImageData imageData = new FormatProviderImageData();

            ExifExtractor exifInfo = new ExifExtractor(fileName);

            DateTime fileLastWriteTime = File.GetLastWriteTimeUtc(fileName);

            if (exifInfo.DateTaken == DateTime.MinValue)
            {
                exifInfo.DateTaken = fileLastWriteTime;
            }

            imageData.FileName = fileName;

            imageData.ImageDescription = exifInfo.ImageDescription;

            imageData.LastModifiedDateTime = fileLastWriteTime;

            imageData.CameraMaker = exifInfo.CameraMaker;
            imageData.CameraModel = exifInfo.CameraModel;

            imageData.SoftwareUsed = exifInfo.SoftwareUsed;

            imageData.ExposureTime = exifInfo.ExposureTime;
            imageData.Aperture = exifInfo.Aperture;

            imageData.ExposureProgram = exifInfo.ExposureProgram;
            imageData.IsoSpeed = exifInfo.IsoSpeed;

            imageData.DateTaken = exifInfo.DateTaken;
            imageData.DateDigitized = exifInfo.DateDigitized;

            imageData.ExposureCompensation = exifInfo.ExposureCompensation;
            imageData.MeteringMode = exifInfo.MeteringMode;
            imageData.LightSource = exifInfo.LightSource;

            imageData.FlashStatus = exifInfo.FlashStatus;
            imageData.FocalLength = exifInfo.FocalLength;

            imageData.ImageWidth = exifInfo.ImageWidth;
            imageData.ImageHeight = exifInfo.ImageHeight;

            imageData.Keywords = exifInfo.Keywords;

            imageData.SourceLabel = exifInfo.SourceLabel;

            return imageData;
        }

        public DateTime GetImageMediaLastUpdated(string fileName)
        {
            return File.GetLastWriteTimeUtc(fileName);
        }

        public string GetImageMediaSignature(string fileName)
        {
            return FileUtilities.GetFileMD5Signature(fileName);
        }

        public virtual void SetImageMetaData(IImageData imageData, String fileName)
        {
            try
            {
                if (string.IsNullOrEmpty(fileName) == true ||
                    File.Exists(fileName) == false)
                {
                    return;
                }

                ExifExtractor exifExtractor = new ExifExtractor(fileName);
                exifExtractor.ImageWidth = imageData.ImageSize.Width;
                exifExtractor.ImageHeight = imageData.ImageSize.Height;

                exifExtractor.DateTaken = imageData.DateTaken;
                exifExtractor.DateDigitized = (imageData.DateDigitized != DateTime.MinValue) ? imageData.DateDigitized : imageData.DateTaken;

                using (Image image = Image.FromFile(fileName, true))
                {
                    Size calculatedSize = ImageAlgo.GetImageProportionalSize(new SizeF(image.Width, image.Height), new SizeF(240, 160));
                    imageData.Thumbnail = new Bitmap(image, calculatedSize.Width, calculatedSize.Height);
                    exifExtractor.Thumbnail = ThumbnailUtilities.ThumbnailToByteArray(imageData.Thumbnail);
                }
                exifExtractor.ImageDescription = imageData.Comment;
                exifExtractor.Save(fileName);

                imageData.FileSignature = FileUtilities.GetFileMD5Signature(fileName);
                imageData.LastModified = File.GetLastWriteTimeUtc(fileName);
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
            }
        }

        public bool Exists(string path)
        {
            return File.Exists(path);
        }

        public bool CanDelete(string path)
        {
            return Exists(path);
        }

        public void Delete(string path)
        {
            try
            {
                Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(path, Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs,
                     Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
            }
        }

        public void CopyTo(IImageData imageData, string newMediaPath)
        {
            File.Copy(imageData.FileName, newMediaPath, false);
        }

        #endregion
    }
}
